简介
- annotation是Java5引入的新特征,它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)关联。
- 通俗来讲,就是为程序的元素加上更直观明了的说明,这些说明信息是与程序的业务逻辑无关,提供给指定的工具或框架使用的。
annotation实际是一种接口,通过Java反射机制来访问annotation信息,并根据这些信息来决定如何使用被注解的程序元素或改变其行为。
annotation类型使用关键字@interface定义,表示继承了java.lang.annotation.Annotation接口,并声明它也是一个接口。
- annotation与接口相似,可以定义常量、静态成员类型(如枚举类)。
- annotation类型的方法必须是、声明为无参数、无异常抛出的。
- 成员方法即为成员属性,方法名为属性名,方法返回值为属性类型。
- 方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或由这些类型之一作为元素的数组。方法后面可以使用default和一个默认值来声明成员的默认值(null除外)。
元注解
- 作用:注解其他注解。
- Java5定义了4个标准的meta-annotation类型,用于对其他annotation作说明。
- @Target
- @Retention
- @Documented
- @Inherited
@Target
- 说明了Annotation所修饰的对象范围,也即是注解的使用范围。annotation可悲用于packages、types(类、接口、枚举、annotation类型)、类型成员(方法、属性、枚举值)、方法参数和本地变量(如循环变量、catch参数),使用target可明确其修饰的目标。
取值(ElementType):
- CONSTRUCTOR:注解在构造器上。
- FIELD:成员属性。
- LOCAL_VARIABLE:局部变量。
- METHOD:方法。
- PACKAGE:包。
- PARAMETER:参数。
- TYPE:type。
实例:
//数据表名注解
@Target(ElementType.TYPE)
public @interface Table {
/**
* 表名,默认值为被注解类名
* @return
*/
public String tableName() default "className";
}
//不映射字段注解
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
@Retention
- 作用:定义了注解被保留的时间长短。某些注解只出现在源代码中,不被编译,一些则被编译到class文件中,但它们可能会被虚拟机忽略,另一些则在加载Class时被读取。使用此元注解可对注解的生命周期进行限制,指明需要在什么级别保存该注解。
取值(RetentionPolicy):
- SOURCE: 在源文件中有效(即保留在源文件)
- CLASS:在class文件中有效。
- RUNTIME:在运行时有效。
实例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
- RetentionPolicy取值为RUNTIME,这样注解处理器可以通过反射,获取该注解的属性值,从而去做一些运行时的逻辑处理。
@Documented
- 作用:使annotation被javadoc类的工具文档化。
- 没有取值可选,直接加在annotation上即可。
@Inherited
- 作用:表示被annotation注解的类其子类也继承了此annotation,但实现了带此annotation的接口的实现类并不会继承该annotation,方法也不会从它重载的方法继承annotation。
- 无取值可选。
自定义注解
- 使用@interface自定义注解,注意不能继承其他的注解或接口。
- 其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,返回值类型就是参数类型。
- annotation里面的参数要求:
- 只能用public或default修饰,如String value()即为default。
- 类型只能是基本数据类型、String、Enum、Class、Annotation和它们之一的数组。
- 如果只有一个参数成员,最好命名为value,则可以直接注入值。
- 实例:
/**
* 水果名称注解
* @author wangsheng
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果颜色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
//颜色枚举
public enum Color{BULE,RED,GREEN};
//颜色属性
Color fruitColor() default Color.GREEN;
}
//使用注解
public class Apple {
@FruitName("Apple") //直接注入值而不用指明属性
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
}
- 非基本类型的注解属性的值不可为null,因此,使用空字符串和0作为默认值是一种常用的做法。也正是因为不能为null,处理器很难判断一个属性是否缺失,所以我们只能定义一些特殊的默认值,例如空字符串或负数来表示某个属性不存在。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
* @return
*/
public int id() default -1;
/**
* 供应商名称
* @return
*/
public String name() default "";
}
- 实例:最后还模拟了一下反射机制对注解的解析。
/**
* 定义作者信息的注解,name和group
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Author {
String name();
String group();
}
/**
* 定义描述信息的注解,value
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Description {
String value();
}
/**
* 使用注解
*/
@Description(value="这是一个有用的工具类") // value可以省略
public class Utility {
@Author(name="wangsheng", group="developer team")
public String work() {
return "work over!";
}
}
/**
*在运行时分析处理annotation类型的信息
*/
public class AnalysisAnnotation {
public static void main(String[] args) {
try {
// 通过运行时反射API获得annotation信息
Class<?> rtClass =
Class.forName("com.wsheng.aggregator.annotation.Utility");
Method[] methods = rtClass.getMethods();
//判断Description注解是否存在
boolean descriptionExist =
rtClass.isAnnotationPresent(Description.class);
if (descriptionExist) {
//从类中获取注解Description
Description description =
(Description)rtClass.getAnnotation(Description.class);
//Utility's Description --- > 这是一个有用的工具类
System.out.println("Utility's Description --- > "
+ description.value());
//从方法中获取Author注解
for (Method method : methods) {
if (method.isAnnotationPresent(Author.class)) {
Author author = (Author)method.getAnnotation(Author.class);
//Utility's Author ---> wangsheng from developer team
System.out.println("Utility's Author ---> " + author.name()
+ " from " + author.group());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
自定义的注解使用其他已定义注解
- 有的时候要在自定义的注解上使用其他定义好的注解,比如:
@Caching(
put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
}
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}
- 把要用的注解取好值加在自定义注解上,然后这个自定义注解加在方法上自然就会拥有@Caching的功能,不过属性就不能改了。
详见笔记:Spring Cache